/*
 * glMatrix.cpp
 *
 *  Created on: 30.10.2012
 *      Author: Martin
 */

#ifdef _WIN32 
#include <windows.h> 
#endif

#include "../dataTypes/Cube.h"
#include "World.h"

namespace World
{

// static declaration of world
World *world;

int counter = 0;
// utility for global counting of boxes
int countBoxes = 0;
int countBoxesVisible = 0;

void worldInit()
{
	world = new World();
}

void deleteWorld()
{
	delete (world);
}

World::World()
{
	//lightCube = NULL;

	// setup projection matrix
	Projection = glm::perspective(45.0f,
			(GLfloat) Engine::screenSettings.screenWidth
					/ (GLfloat) Engine::screenSettings.screenHeight, 0.1f, 150.0f);

	// setup default view matrix
	ViewIdentity();

	// setup lightning
	setLightning();

	// setup shadow projection
	setShadowProjection();

	set2DProjection();

	// setup bias matrix
	biasMatrix = glm::mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5,
			0.0, 0.5, 0.5, 0.5, 1.0);
}

World::~World()
{
}

void World::extractFrustum()
{
	float t;

	/* Combine the two matrices (multiply projection by modelview) */
	glm::mat4 ProjView = Projection * View;

	/* Extract the numbers for the RIGHT plane */
	frustum[0][0] = ProjView[0][3] + ProjView[0][0];
	frustum[0][1] = ProjView[1][3] + ProjView[1][0];
	frustum[0][2] = ProjView[2][3] + ProjView[2][0];
	frustum[0][3] = ProjView[3][3] + ProjView[3][0];

	/* Normalize the result */
	t = sqrt(
			frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1]
					+ frustum[0][2] * frustum[0][2]);
	frustum[0][0] /= t;
	frustum[0][1] /= t;
	frustum[0][2] /= t;
	frustum[0][3] /= t;

	/* Extract the numbers for the LEFT plane */
	frustum[1][0] = ProjView[0][3] - ProjView[1][0];
	frustum[1][1] = ProjView[1][3] - ProjView[1][0];
	frustum[1][2] = ProjView[2][3] - ProjView[2][0];
	frustum[1][3] = ProjView[3][3] - ProjView[3][0];

	/* Normalize the result */
	t = sqrt(
			frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1]
					+ frustum[1][2] * frustum[1][2]);
	frustum[1][0] /= t;
	frustum[1][1] /= t;
	frustum[1][2] /= t;
	frustum[1][3] /= t;

	/* Extract the BOTTOM plane */
	frustum[2][0] = ProjView[0][3] + ProjView[0][1];
	frustum[2][1] = ProjView[1][3] + ProjView[1][1];
	frustum[2][2] = ProjView[2][3] + ProjView[2][1];
	frustum[2][3] = ProjView[3][3] + ProjView[3][1];

	/* Normalize the result */
	t = sqrt(
			frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1]
					+ frustum[2][2] * frustum[2][2]);
	frustum[2][0] /= t;
	frustum[2][1] /= t;
	frustum[2][2] /= t;
	frustum[2][3] /= t;

	/* Extract the TOP plane */
	frustum[3][0] = ProjView[0][3] - ProjView[0][1];
	frustum[3][1] = ProjView[1][3] - ProjView[1][1];
	frustum[3][2] = ProjView[2][3] - ProjView[2][1];
	frustum[3][3] = ProjView[3][3] - ProjView[3][1];

	/* Normalize the result */
	t = sqrt(
			frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1]
					+ frustum[3][2] * frustum[3][2]);
	frustum[3][0] /= t;
	frustum[3][1] /= t;
	frustum[3][2] /= t;
	frustum[3][3] /= t;

	/* Extract the FAR plane */
	frustum[4][0] = ProjView[0][3] - ProjView[0][2];
	frustum[4][1] = ProjView[1][3] - ProjView[1][2];
	frustum[4][2] = ProjView[2][3] - ProjView[2][2];
	frustum[4][3] = ProjView[3][3] - ProjView[3][2];

	/* Normalize the result */
	t = sqrt(
			frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1]
					+ frustum[4][2] * frustum[4][2]);
	frustum[4][0] /= t;
	frustum[4][1] /= t;
	frustum[4][2] /= t;
	frustum[4][3] /= t;

	/* Extract the NEAR plane */
	frustum[5][0] = ProjView[0][3] + ProjView[0][2];
	frustum[5][1] = ProjView[1][3] + ProjView[1][2];
	frustum[5][2] = ProjView[2][3] + ProjView[2][2];
	frustum[5][3] = ProjView[3][3] + ProjView[3][2];

	/* Normalize the result */
	t = sqrt(
			frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1]
					+ frustum[5][2] * frustum[5][2]);
	frustum[5][0] /= t;
	frustum[5][1] /= t;
	frustum[5][2] /= t;
	frustum[5][3] /= t;
}

bool World::PointInFrustum(glm::vec3 X)
{
	int p;

	for (p = 0; p < 6; p++)
		if (frustum[p][0] * X.x + frustum[p][1] * X.y + frustum[p][2] * X.z
				+ frustum[p][3] <= 0)
			return false;

	return true;
}

float World::SphereInFrustum(glm::vec3 X, float radius)
{
	int p;
	float d;

	for (p = 0; p < 6; p++)
	{
		d = frustum[p][0] * X.x + frustum[p][1] * X.y + frustum[p][2] * X.z
				+ frustum[p][3];
		if (d <= -radius)
			return 0;
	}
	return d + radius;
}

bool World::CubeInFrustum(DataTypes::Cube cube)
{
	int p;

	for (p = 0; p < 6; p++)
	{
		if (frustum[p][0] * cube.getVertexes()[1].x
				+ frustum[p][1] * cube.getVertexes()[1].y
				+ frustum[p][2] * cube.getVertexes()[1].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[2].x
				+ frustum[p][1] * cube.getVertexes()[2].y
				+ frustum[p][2] * cube.getVertexes()[2].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[5].x
				+ frustum[p][1] * cube.getVertexes()[5].y
				+ frustum[p][2] * cube.getVertexes()[5].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[6].x
				+ frustum[p][1] * cube.getVertexes()[6].y
				+ frustum[p][2] * cube.getVertexes()[6].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[0].x
				+ frustum[p][1] * cube.getVertexes()[0].y
				+ frustum[p][2] * cube.getVertexes()[0].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[3].x
				+ frustum[p][1] * cube.getVertexes()[3].y
				+ frustum[p][2] * cube.getVertexes()[3].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[4].x
				+ frustum[p][1] * cube.getVertexes()[4].y
				+ frustum[p][2] * cube.getVertexes()[4].z + frustum[p][3] > 0)
			continue;
		if (frustum[p][0] * cube.getVertexes()[7].x
				+ frustum[p][1] * cube.getVertexes()[7].y
				+ frustum[p][2] * cube.getVertexes()[7].z + frustum[p][3] > 0)
			continue;
		return false;
	}
	return true;
}

void World::setLightning()
{
	dLight = glm::vec3(1.0, 1.0, 1.0);
	aLight = glm::vec3(0.2, 0.2, 0.2);
	sLight = glm::vec3(1.0, 1.0, 1.0);

	posLight = glm::vec3(250.0, 250.0, 250.0);

	lightPower = 10000.0f;

	//lightCube = new DataTypes::Cube(posLight, 1.0);
}

void World::drawLight()
{
	//if (lightCube != NULL)
	//	lightCube->drawCube();
}

void World::setProjection(glm::mat4 proj)
{
	Projection = proj;
}

void World::set2DProjection()
{
	twoDProjection = glm::ortho<float>(0.0, Engine::screenSettings.screenWidth,
			Engine::screenSettings.screenHeight, 0.0, -1.0, 1.0);
}

glm::mat4 World::get2DProjection()
{
	return twoDProjection;
}

void World::setShadowProjection()
{
	shadowProjection = glm::ortho<float>(-120, 120, -120, 120, -100, 1100);
}

void World::update()
{
	setShadowView(ShadowlookToPoint);
}

void World::setShadowView(glm::vec3 lookToPoint)
{
	ShadowlookToPoint = lookToPoint;

	shadowView = glm::lookAt(posLight, lookToPoint, glm::vec3(0, 1, 0));
}

glm::mat4 World::getBiasMVP()
{
	return biasMatrix * shadowProjection * shadowView * glm::mat4(1.0);
}

glm::mat4 World::getShadowProjection()
{
	return shadowProjection;
}

glm::mat4 World::getShadowView()
{
	return shadowView;
}

void World::setLightPos(glm::vec3 pos)
{
	posLight = pos;
}

void World::ViewIdentity()
{
	View = glm::mat4(1.0);
}

glm::mat4 World::getProjection()
{
	return Projection;
}

glm::mat4 World::getView()
{
	return View;
}

void World::setView(glm::mat4 v)
{
	View = v;
}

glm::vec3 World::getLightPos()
{
	return posLight;
}

glm::vec3 World::getLightDif()
{
	return dLight;
}

glm::vec3 World::getLightAmb()
{
	return aLight;
}

glm::vec3 World::getLightSpe()
{
	return sLight;
}

float World::getLightPower()
{
	return lightPower;
}

}

